package cn.geeltool.poi.csv;

import cn.geeltool.poi.converter.DefaultConvertible;
import cn.geeltool.poi.exceptions.Excel4JException;
import cn.geeltool.poi.exceptions.Excel4jReadException;
import cn.geeltool.poi.handler.ExcelHeader;
import cn.geeltool.poi.utils.Utils;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
/**
 * csv操作
 * author : jiangdi
 * date : 2021/3/8  10:28
 */

public class CsvUtils{

    /**
     * 单例模式
     * 通过{@link CsvUtils#getInstance()}获取对象实例
     */
    private static volatile CsvUtils excelUtils;

    private CsvUtils() {
    }
    /**
     * 双检锁保证单例
     * @return csv对象
     */
    public static cn.geeltool.poi.csv.CsvUtils getInstance() {
        if (null == excelUtils) {
            synchronized (cn.geeltool.poi.csv.CsvUtils.class) {
                if (null == excelUtils) {
                    excelUtils = new cn.geeltool.poi.csv.CsvUtils();
                }
            }
        }
        return excelUtils;
    }



    /*---------------------------------------1.基于注解的CSV读取--------------------------------------------------*/
    /*  一. 操作流程 ：                                                                                           */
    /*      1) 读取表头信息,与给出的Class类注解匹配                                                                 */
    /*      2) 读取表头下面的数据内容, 按行读取, 并映射至java对象                                                     */
    /*  二. 参数说明                                                                                              */
    /*      *) path             =>      待读取文件路径                                                            */
    /*      *) is               =>      待读取文件流                                                              */
    /*      *) clazz            =>      映射对象                                                                  */

    /**
     * 基于注解读取CSV文件
     *
     * @param <T>   对象范型
     * @param path  待读取文件路径
     * @param clazz 待绑定的类(绑定属性注解{@link cn.geeltool.poi.annotation.ExcelField})
     * @return 返回转换为设置绑定的java对象集合
     * @throws Excel4jReadException exception
     */
    public <T> List<T> readCSV2Objects(String path, Class<T> clazz) {

        try (InputStream is = new FileInputStream(new File(path))) {
            return readCSVByMapHandler(is, clazz);
        } catch (IOException | Excel4JException e) {
            throw new Excel4jReadException("read [" + path + "] CSV Error: ", e);
        }
    }

    /**
     * 基于注解读取CSV文件
     *
     * @param is    待读取文件输入流
     * @param clazz 待绑定的类(绑定属性注解{@link cn.geeltool.poi.annotation.ExcelField})
     * @return 返回转换为设置绑定的java对象集合
     * @throws Excel4jReadException exception
     */
    public <T> List<T> readCSV2Objects(InputStream is, Class<T> clazz) {

        try {
            return readCSVByMapHandler(is, clazz);
        } catch (Excel4JException | IOException e) {
            throw new Excel4jReadException("read CSV Error: ", e);
        }
    }

    // 读取csv
    private <T> List<T> readCSVByMapHandler(InputStream is, Class<T> clazz)
            throws IOException, Excel4JException {

        List<T> records = new ArrayList<>();

        List<ExcelHeader> headers = Utils.getHeaderList(clazz);
        if (null == headers || headers.size() <= 0) {
            throw new Excel4jReadException("[" + clazz + "] must configuration @ExcelFiled");
        }
        String[] csvHeaders = new String[headers.size()];
        for (int i = 0; i < headers.size(); i++) {
            csvHeaders[i] = headers.get(i).getTitle();
        }
        CSVFormat format = CSVFormat.EXCEL.withHeader(csvHeaders).withSkipHeaderRecord(true);
        try (Reader read = new InputStreamReader(is, "gb2312");
             CSVParser parser = new CSVParser(read, format)) {
            for (CSVRecord _parser : parser) {
                T obj;
                try {
                    obj = clazz.newInstance();
                } catch (InstantiationException | IllegalAccessException e) {
                    throw new Excel4jReadException(e);
                }
                for (ExcelHeader header : headers) {
                    String value = _parser.get(header.getTitle());
                    Object objectVal;
                    String filed = header.getFiled();
                    // 读取转换器
                    if (null != header.getReadConverter() &&
                            header.getReadConverter().getClass() != DefaultConvertible.class) {
                        objectVal = header.getReadConverter().execRead(value);
                    } else {
                        // 默认转换
                        objectVal = Utils.str2TargetClass(value, header.getFiledClazz());
                    }
                    Utils.copyProperty(obj, filed, objectVal);
                }
                records.add(obj);
            }
        }
        return records;
    }
}
